//////////////////////////////////////////////////////
// 程序名称：用中点画圆法画椭圆弧（选做）
// 功 能：画一些椭圆弧
// 编译环境：Visual Studio 2017，EasyX_20190219(beta)
// 作 者：xyqlx<mxxyqlx@qq.com>
// 最后修改：2019-3-7
#include <conio.h>
#include <graphics.h>
#include <cmath>

void StdEllipseArc(int x0,int y0,int a,int b,double sangle,double eangle,COLORREF color);
double PI = acos(0) * 2;

int main()
{
	// 创建绘图窗口，大小为 640x480 像素
	initgraph(640, 480);
	//填充背景为白色
	setbkcolor(WHITE);
	cleardevice();
	//开始绘制
	setlinecolor(BLUE);

	line(320, 0, 320, 480);
	line(0, 240, 640, 240);

	double start[] = {1.2, 1.9, 3.4, 6.0,0.0,0.0};
	double end[] = {1.7, 3.4, 5.9, 3.4,1.0,6.2};
	//画一些辅助线
	setlinecolor(YELLOW);
	for (int i = 0; i < 6;++i){
		line(320, 240, 320 + 200 * cos(start[i]), 240 + 200 * sin(start[i]));
		line(320, 240, 320 + 200 * cos(end[i]), 240 + 200 * sin(end[i]));
	}

	for (int i = 0; i < 6; ++i)
	{
		StdEllipseArc(320, 240, 100 + i * 10, 100 - i * 10, start[i], end[i], RED);
	}
	
	//结束绘制
	_getch();              // 按任意键继续
	closegraph();          // 关闭绘图窗口
	return 0;
}

//判断画点
inline void putCirclePoint(int x,int y,int seg[2],COLORREF color){
	if (seg[0] <= x && x <= seg[1]) {
		putpixel(x, y, color);
	}	
}
//x0,y0:椭圆的中心 a,b:横轴和纵轴的长 sangle,eangle:椭圆弧的始角度和终角度，函数会从前者向后者顺时针画一段小于整椭圆的弧
void StdEllipseArc(int x0,int y0,int a,int b,double sangle,double eangle,COLORREF color){
	int x, y, d;
	//记录弧在各象限的范围
	int point[4][2] = {{x0,x0+a},{x0-a,x0},{x0-a,x0},{x0,x0+a}};
	//使角度参数规范化为0~2PI
	if(sangle < 0)
		sangle += int(-sangle / 2 / PI + 1) *2* PI;
	if(sangle > 2*PI)
		sangle -= int(sangle / 2 / PI)*2*PI;
	if(eangle < 0)
		eangle += int(-eangle / 2 / PI + 1) * 2*PI;
	if(eangle > 2*PI)
		eangle -= int(eangle / 2 / PI)*2*PI;
	x = int(sangle * 2 / PI);
	y = int(eangle * 2 / PI);
	//计算弧在不出现的象限的范围
	if(x < y || (x==y && sangle < eangle)){
		for (int i = 0; i < 4;++i){
			if(i<x || i>y){
				point[i][0] = 1;
				point[i][1] = 0;
			}
		}
		
	}else{
		for (int i = 0; i < 4;++i){
			if(i>y && i<x)
			{
				point[i][0] = 1;
				point[i][1] = 0;
			}
		}
	}
	//修正角度并计算范围
	if(sangle > PI/2 && sangle<3*PI/2)
		point[x][x==0 || x==1] = x0 - a / sqrt(1 + a*a*pow(tan(sangle),2)/b/b) + 0.5;
	else
		point[x][x==0 || x==1] = x0 + a / sqrt(1 + a*a*pow(tan(sangle),2)/b/b) + 0.5;
	if(eangle > PI/2 && eangle<3*PI/2)
		point[y][y==2 || y==3] = x0 - a / sqrt(1 + a*a*pow(tan(eangle),2)/b/b) + 0.5;
	else
		point[y][y==2 || y==3] = x0 + a / sqrt(1 + a*a*pow(tan(eangle),2)/b/b) + 0.5;
	//开始画图
	x = 0;
	y = b;
	int px = a * a / sqrt(a * a + b * b);
	int py = b * b / sqrt(a * a + b * b);
	d = b*b+(-b+0.25)*a*a;
	putCirclePoint(x + x0, y + y0, point[0], color);
	putCirclePoint(x + x0, -y + y0, point[3], color);
	putCirclePoint(-x + x0, y + y0, point[1], color);
	putCirclePoint(-x + x0, -y + y0, point[2], color);
	while (x <= px)
	{
		if (d < 0)
		{
			d += (2*x+3)*b*b;
			x++;
		}
		else
		{
			d += (2*x+3)*b*b+(-2*y+2)*a*a;
			x++;
			y--;
		}
		putCirclePoint(x + x0, y + y0, point[0], color);
		putCirclePoint(x + x0, -y + y0, point[3], color);
		putCirclePoint(-x + x0, y + y0, point[1], color);
		putCirclePoint(-x + x0, -y + y0, point[2], color);
	}
	d = 1.0f * b * b * (x + 0.5) * (x + 0.5) + a * a * (y - 1) * (y - 1) - a * a * b * b;
	while (y > 0)
	{
		if (d < 0)
		{
			d += (-2*y+3)*a*a+(2*x+2)*b*b;
			x++;
			y--;
		}
		else
		{
			d += (-2*y+3)*a*a;
			y--;
		}
		putCirclePoint(x + x0, y + y0, point[0], color);
		putCirclePoint(x + x0, -y + y0, point[3], color);
		putCirclePoint(-x + x0, y + y0, point[1], color);
		putCirclePoint(-x + x0, -y + y0, point[2], color);
	}
}